from __future__ import with_statement

import sys
import traceback
import time
from Queue import Queue
from threading import Thread, Lock, Event


class BGThread(Thread): # TODO compact db

    def __init__(self):
        Thread.__init__(self)
        self._lock = Lock()
        self._tasks = Queue()
        self._started = False
        self._errcb = None
        self.setDaemon(True)

    def _start(self):
        with self._lock:
            if not self._started:
                self.start()
                self._started = True

    def quit(self):
        if self.isAlive():
            self._tasks.put(None)
            self.join()

    def set_error_cb(self, callback):
        with self._lock:
            self._errcb = callback

    def queue_join(self):
        self._tasks.join()

    def task(self, task):
        self._start()
        self._tasks.put(task)

    def run(self):
        while True:
            task = self._tasks.get()
            if task is None:
                self._tasks.task_done()
                return
            try:
                task()
            except: #pragma: no cover
                with self._lock:
                    if self._errcb is not None:
                        err = sys.exc_info()
                        try:
                            self._errcb(err)
                        except:
                            print "Error executing Kineta error callback: %s"\
                                    % traceback.format_exc()
                        finally:
                            del err
                    else:
                        print "Error in KinetaDB background thread: %s"\
                                % traceback.format_exc()
            finally:
                self._tasks.task_done()


class LockDetectThread(Thread):

    def __init__(self, db):
        Thread.__init__(self)
        self._db = db
        self._event = Event()
        self.setDaemon(True)
        self.start()

    def quit(self):
        self._event.set()
        self.join()

    def run(self):
        while True:
            try:
                self._db.lock_detect()
                self._event.wait(10)
                if self._event.isSet():
                    return
            except: #pragma: no cover
                print "Error in KinetaDB deadlock detection thread: %s"\
                        % traceback.format_exc()
                time.sleep(10)
